import json
import time
from typing import Dict, Any

from apscheduler.schedulers.blocking import BlockingScheduler

from common.entitys.monitor import MonitorType, Monitor
from common.enums.Enums import FormCons
from database.mapper.monitor_result import MonitorResult
from database.mapper.monitor_strategy_config import StrategyCons
from utils.logger import Logger
from utils import date_util, msg_util, caiptal_util, parse_util, dict_util
from database.mapper import stock_monitor, monitor_result, user_info, boards_info, monitor_strategy_config, \
    stock_info as sdi_mapper
from service import training_model

# tik_cycle = get_tik_cycle([0, 5, 10, 20, 40, 80, 160, 320])
tik_cycle = [0, 300, 300, 600, 600, 900, 1200, 1500, 2400, 2700, 4800, 5100, 9600, 9900, 19200, 19500]

frequency_config = 300  # 定时任务时间 单位秒
logger = Logger('实时监控任务总').get_log()


# 买入匹配
# 1.是否为当前热点
# 2.主力资金情况 (净流入):
#     2.1.分歧情况
#     2.2.主力资金和价格涨幅的分歧情况
# 3.市盈率和业绩情况
# 4.看下板块1234发力状态买

# 开启监控定时任务
def start_task():
    # 先执行一次再开启定时任务 方便一次启动监控
    monitor_task()
    # 添加任务,时间间隔180S
    scheduler = BlockingScheduler()
    scheduler.add_job(monitor_task, 'interval', seconds=frequency_config, id='monitor_task')
    scheduler.start()


# 监控定时任务
def monitor_task():
    print("task_c执行时间" + date_util.get_date_time_str())
    task_num = route_task()
    if task_num == 0:
        return
    elif task_num == 1:
        monitor_conf()


# 检查有效时间
def route_task():
    if not date_util.valid_day(date_util.get_date_str()):
        return 0
    date_time = date_util.get_date_time()
    hour = date_time.hour
    minute = date_time.minute
    if 9 <= hour < 15 and not (hour == 9 and minute < 35) \
            and not (hour == 11 and minute > 30) and not (hour == 12):
        return 1
    return 0


# 监控所有的 task_c
def monitor_all():
    sm_dict = query_monitor_info()
    stock_all = sdi_mapper.selectall()
    for s_info in stock_all:
        code = s_info.get('code')
        name = s_info.get('name')
        if code in sm_dict.keys():
            continue
        try:
            # 处理触发情况
            deal_trigger(Monitor(code, name))
            # 删除缓存
            training_model.del_module_doc_cache(code)
            time.sleep(1)
        except Exception as e:
            logger.error(e)
            logger.error('任务C系统运行异常' + code + name)


# 监控配置的监控
def monitor_conf():
    sm_dict = query_monitor_info()
    msg_dict = {}
    for key in sm_dict.keys():
        try:
            monitor = Monitor(sm_dict.get(key).get('code'), sm_dict.get(key).get('name'))
            # 处理触发情况
            deal_trigger(monitor)
            # 处理监控策略
            deal_strategy(monitor)
            # 处理消息
            deal_msg(monitor, msg_dict)
        except Exception as e:
            logger.error(e)
            logger.error('任务C系统运行异常' + sm_dict.get(key).get('code') + sm_dict.get(key).get('name'))
    send_msg(msg_dict)  # 发送用户消息提示


# 获取消息结构
def gen_msg_dict():
    user_list = user_info.selectall()
    msg_dict: Dict[str, Dict[Any, Any]] = {'search': {}}
    for user in user_list:
        prompt_code = user.get('prompt_code')
        msg_dict['search'][prompt_code] = []
        user_monitor_stock = stock_monitor.select_all(prompt_code)
        for monitor_s in user_monitor_stock:
            msg_dict['search'][prompt_code].append(monitor_s.get('code'))
    return msg_dict


# 扩展监控
def query_monitor_info():
    sm_list = stock_monitor.select_distinct()
    code_list = [i['code'] for i in sm_list]
    board_list = boards_info.select_board_by_stocks(code_list)
    boards_info_dict = caiptal_util.board_flow_info()
    board_info_list = []
    for b in board_list:
        board_info = boards_info_dict.get(b)
        if board_info.get('main_io') < 1000000:
            continue
        board_info_list.append(board_info)
    board_info_dict = {}
    board_info_list = sorted(board_info_list, key=lambda x: x['main_io'], reverse=True)
    for i in range(min(30, len(board_info_list))):
        board_info_dict[board_info_list[i].get('board_code')] = board_info_list[i]
    board_info_list = sorted(board_info_list, key=lambda x: x['zf'], reverse=True)
    for i in range(min(30, len(board_info_list))):
        board_info_dict[board_info_list[i].get('board_code')] = board_info_list[i]
    board_info_list = sorted(parse_util.dict_to_list(boards_info_dict), key=lambda x: x['main_io'], reverse=True)
    for i in range(min(10, len(board_info_list))):
        board_info_dict[board_info_list[i].get('board_code')] = board_info_list[i]
    for key in board_info_dict:
        bd_list = boards_info.select_board_detail_val_top_n(key, 3)
        for i in range(min(len(bd_list), 3)):
            if bd_list[i].get('total_val') < 200:
                continue
            sm_list.append(bd_list[i])
    return parse_util.list_to_dict(sm_list, 'code')


# 处理触发
# 数据一定要硬
def deal_trigger(monitor: Monitor):
    # 突破均线 处理
    if ((monitor.get_form_info().best_form == FormCons.js and monitor.get_form_info().max_front > 70)
        or (monitor.get_form_info().best_form == FormCons.sj and monitor.get_form_info().max_front < 66)
        or monitor.get_form_info().best_form == FormCons.hj
        or monitor.get_form_info().best_form == FormCons.j) \
            and monitor.get_form_info().zf > 5 \
            and monitor.get_form_info().max_score > 0.5:
        if monitor.get_last_max_price() < monitor.get_ave5() < monitor.get_rt_price():
            monitor.break_ave5 = True
            monitor.bs = 1
            monitor.update_flag = True
        if monitor.get_last_max_price() < monitor.get_ave10() < monitor.get_rt_price():
            monitor.break_ave10 = True
            monitor.bs = 1
            monitor.update_flag = True
    # 跌破均线 处理
    if ((monitor.get_form_info().best_form == FormCons.sj and monitor.get_form_info().max_front > 50)
        or (monitor.get_form_info().best_form == FormCons.js and 30 < monitor.get_form_info().max_front < 70)
        or monitor.get_form_info().best_form == FormCons.s
        or monitor.get_form_info().best_form == FormCons.hs) \
            and monitor.get_form_info().zf > 5 \
            and monitor.get_form_info().max_score > 0.5 \
            and (monitor.get_form_macd3() == '\\' or monitor.get_form_macd3() == '^'):
        if monitor.get_last_min_price() > monitor.get_ave5() > monitor.get_rt_price():
            monitor.below_ave5 = True
            monitor.bs = 2
            monitor.update_flag = True
        if monitor.get_last_min_price() > monitor.get_ave10() > monitor.get_rt_price():
            monitor.below_ave10 = True
            monitor.bs = 2
            monitor.update_flag = True

    # 大资金 处理
    if (monitor.get_train_model().zf_earn_m_io > 0
        and monitor.get_train_model().zf_price_m_io > 0
        and monitor.get_train_model().zf_earn_m_io / monitor.get_train_model().zf_price_m_io > 0.7) \
            or (monitor.get_train_model().zf_earn_m_io < 0
                and monitor.get_train_model().zf_price_m_io < 0
                and monitor.get_train_model().zf_earn_m_io / monitor.get_train_model().zf_price_m_io < 0.4) \
            or (monitor.get_train_model().zf_earn_m_io > 0
                and monitor.get_train_model().zf_price_m_io < 0):
        buy_jfg = int(monitor.get_train_model().buy_jfg)
        sel_jfg = int(monitor.get_train_model().sel_jfg)
        # 资金离散度 计算稳定程度
        if monitor.get_flow_sum_dict().get(buy_jfg) < 0 \
                and monitor.get_flow_sum_dict().get(buy_jfg - 1) + monitor.get_rt_main_io() > 1000000 \
                and monitor.get_flow_sum_dict().get(sel_jfg - 1) + monitor.get_rt_main_io() > 0 \
                and monitor.get_rt_main_io() > 1000000:  # 当天必须是流入的
            monitor.main_io_buy = True
            monitor.main_io_buy_model = str(buy_jfg) + ':' + str(sel_jfg)
            monitor.bs = 1
            monitor.update_flag = True
        if monitor.get_flow_sum_dict().get(sel_jfg) > 0 \
                and monitor.get_flow_sum_dict().get(sel_jfg - 1) + monitor.get_rt_main_io() < -1000000 \
                and monitor.get_rt_main_io() < -1000000:
            monitor.main_io_sell = True
            monitor.main_io_sell_model = str(buy_jfg) + ':' + str(sel_jfg)
            monitor.bs = 2
            monitor.update_flag = True

    # 资金和价格倒挂情况
    if monitor.get_rt_price() < monitor.get_last_sp_price() and monitor.get_rt_main_io() > 1000000:
        print(monitor.code + monitor.name + '资金和价格倒挂')
        monitor.main_io_buy_model += '资金和价格倒挂'

    # 数据库更新或者插入
    if monitor.update_flag:
        monitor_result_old = monitor_result.select_by_date_type(monitor.code, monitor.rq, MonitorType.four)
        if monitor_result_old is not None:
            old_mr = Monitor(monitor.code, monitor.name)
            old_mr.__dict__ = eval(
                monitor_result_old.monitor_result.replace('false', 'False').replace('true', 'True').replace('null',
                                                                                                            'None'))
            judge_prompt(old_mr, monitor)  # 比较差异
        else:
            monitor.prompt = True
        if monitor.prompt:
            monitor_result.up_or_in_by_code_rq(
                MonitorResult(monitor.code, monitor.rq, 'task_c', MonitorType.four,
                              json.dumps(monitor, default=lambda o: o.__dict__), date_util.get_date_time()))


# 比较两次变化
def judge_prompt(old: Monitor, new: Monitor):
    if (old.main_io_sell ^ new.main_io_sell) \
            or (old.main_io_buy ^ new.main_io_buy) \
            or ((old.below_ave5 ^ new.below_ave5) and old.rq != new.rq) \
            or ((old.break_ave5 ^ new.break_ave5) and old.rq != new.rq) \
            or ((old.break_ave10 ^ new.break_ave10) and old.rq != new.rq) \
            or ((old.below_ave10 ^ new.below_ave10) and old.rq != new.rq):
        new.prompt = True


# 处理消息
def deal_msg(monitor: Monitor, msg_dict) -> dict:
    if ((monitor.break_ave5 or monitor.below_ave5 or monitor.break_ave10 or monitor.below_ave10 or monitor.main_io_sell
         or monitor.main_io_buy) and monitor.prompt) \
            or len(monitor.strategy_info) != 0:  # 配置的详细监控
        msg_dict[monitor.code] = {}
        msg_dict[monitor.code]['编码'] = monitor.code
        msg_dict[monitor.code]['名称'] = monitor.name
        msg_dict[monitor.code]['最新价格'] = monitor.get_rt_price()
        msg_dict[monitor.code]['五日均线'] = monitor.get_ave5()
        msg_dict[monitor.code]['十日均线'] = monitor.get_ave10()
        if monitor.bs == 1:
            msg_dict[monitor.code]['买卖'] = '买入'
        if monitor.bs == 2:
            msg_dict[monitor.code]['买卖'] = '卖出'
        if monitor.break_ave5:
            msg_dict[monitor.code]['提示1'] = '突破五日均线'
        if monitor.break_ave10:
            msg_dict[monitor.code]['提示2'] = '突破十日均线'
        if monitor.below_ave5:
            msg_dict[monitor.code]['提示3'] = '跌破五日均线'
        if monitor.below_ave10:
            msg_dict[monitor.code]['提示4'] = '跌破十日均线'
        if monitor.main_io_sell:
            msg_dict[monitor.code]['模式'] = monitor.main_io_sell_model
            msg_dict[monitor.code]['提示5'] = '主力资金卖出'
        if monitor.main_io_buy:
            msg_dict[monitor.code]['模式'] = monitor.main_io_buy_model
            msg_dict[monitor.code]['提示6'] = '主力资金买入'
        for i in range(len(monitor.strategy_info)):
            user_code = monitor.strategy_info[i].get('user_code')
            key = monitor.code + user_code + '监控策略'
            if key not in msg_dict.keys():
                msg_dict[monitor.code + user_code] = []
            msg_dict[monitor.code + user_code].append(monitor.strategy_info[i])
    return msg_dict


def send_msg(msg_dict: dict):
    user_list = user_info.selectall()
    for user in user_list:
        prompt_code = user.get('prompt_code')
        user_monitor_stock = stock_monitor.select_all(prompt_code)
        msg = ''
        for monitor_s in user_monitor_stock:
            msg_info = msg_dict.get(monitor_s.get('code'))
            if msg_info is None:
                continue
            if msg_info.get('买卖') == '卖出' and monitor_s.get('total_hand') == 0:
                continue
            for key in msg_info:
                msg += key + ' : ' + str(msg_info[key]) + '\n'

            # 监控策略
            if monitor_s.get('code') + prompt_code in msg_dict.keys():
                strategy_info_list = msg_dict.get(monitor_s.get('code') + prompt_code)
                for i in range(len(strategy_info_list)):
                    msg += '策略' + str(i + 1) + ' : ' + strategy_info_list[i].get('msg') + '\n'

            msg += '\n'
        # 处理消息
        msg_util.miao_msg(prompt_code, msg)


# 处理监控策略
def deal_strategy(monitor):
    msc_list = monitor_strategy_config.select_by_stock(monitor.code)
    cur_time = date_util.get_date_time()
    for msc in msc_list:
        strategy_type = msc.get(StrategyCons.type)
        bs = msc.get(StrategyCons.bs_flag)  # 1：买入 2：卖出
        trigger_time = dict_util.get_default(msc, StrategyCons.trigger_time, cur_time)
        diff_seconds = int(cur_time.timestamp() - trigger_time.timestamp())
        if strategy_type == 1:
            price = float(msc.get(StrategyCons.conf_info).get(StrategyCons.stockprice))
            pricetype = msc.get(StrategyCons.conf_info).get(StrategyCons.pricetype)
            if (pricetype == '1' and float(monitor.get_rt_k_line(16)[0].get('min')) < price) or \
                    (pricetype == '2' and float(monitor.get_rt_k_line(16)[0].get('max')) > price):
                if in_valid_time(diff_seconds):
                    monitor.strategy_info.append({'msg': msc.get(StrategyCons.conf_info).get(StrategyCons.promptinfo),
                                                  'user_code': msc.get(StrategyCons.user_code)})
                if diff_seconds == 0:
                    monitor_strategy_config.update_trigger_by_id(msc.get(StrategyCons.id), cur_time)
        # 均线价格
        elif strategy_type == 2:
            avedays = int(msc.get(StrategyCons.conf_info).get(StrategyCons.avedays))
            ave_price = monitor.get_ave_price(avedays)
            #  [{'key': '冲破', 'value': '1'}, {'key': '跌破', 'value': '2'},
            #  {'key': '稳高', 'value': '3'}, {'key': '稳低', 'value': '4'}]
            avetype = msc.get(StrategyCons.conf_info).get(StrategyCons.avetype)
            if (avetype == '1' and float(monitor.get_rt_k_line(16)[0].get('max')) > ave_price) or \
                    (avetype == '2' and float(monitor.get_rt_k_line(16)[0].get('min')) < ave_price) or \
                    (avetype == '3' and float(monitor.get_rt_k_line(16)[0].get('min')) > ave_price) or \
                    (avetype == '4' and float(monitor.get_rt_k_line(16)[0].get('max')) < ave_price):
                if in_valid_time(diff_seconds):
                    monitor.strategy_info.append({'msg': msc.get(StrategyCons.conf_info).get(StrategyCons.promptinfo),
                                                  'user_code': msc.get(StrategyCons.user_code)})
                if diff_seconds == 0:
                    monitor_strategy_config.update_trigger_by_id(msc.get(StrategyCons.id), cur_time)
        # 大资金
        elif strategy_type == 3:
            iodays = int(msc.get(StrategyCons.conf_info).get(StrategyCons.iodays))
            main_io = monitor.get_main_io_days(iodays)
            iotype = msc.get(StrategyCons.conf_info).get(StrategyCons.iotype)
            # [{'key': '转正', 'value': '1'}, {'key': '转负', 'value': '2'}]
            if (iotype == '1' and main_io > 0 and monitor.get_rt_main_io() > 0) or \
                    (iotype == '2' and main_io < 0 and monitor.get_rt_main_io() < 0):
                if in_valid_time(diff_seconds):
                    monitor.strategy_info.append({'msg': msc.get(StrategyCons.conf_info).get(StrategyCons.promptinfo),
                                                  'user_code': msc.get(StrategyCons.user_code)})
                if diff_seconds == 0:
                    monitor_strategy_config.update_trigger_by_id(msc.get(StrategyCons.id), cur_time)
        elif strategy_type == 4:
            macdtype = msc.get(StrategyCons.conf_info).get(StrategyCons.macdtype)
            # [{'key': '拐高', 'value': '1'}, {'key': '拐底', 'value': '2'},
            #  {'key': '升高', 'value': '3'}, {'key': '降低', 'value': '4'}]
            if (macdtype == '1' and monitor.get_form_macd3() == 'v') or \
                    (macdtype == '2' and monitor.get_form_macd3() == '^') or \
                    (macdtype == '3' and monitor.get_form_macd3() == '/') or \
                    (macdtype == '4' and monitor.get_form_macd3() == '\\'):
                if in_valid_time(diff_seconds):
                    monitor.strategy_info.append({'msg': msc.get(StrategyCons.conf_info).get(StrategyCons.promptinfo),
                                                  'user_code': msc.get(StrategyCons.user_code)})
                if diff_seconds == 0:
                    monitor_strategy_config.update_trigger_by_id(msc.get(StrategyCons.id), cur_time)


# 是否在提示有效期内
def in_valid_time(diff_seconds):
    for i in range(int(len(tik_cycle) / 2)):
        if tik_cycle[i * 2] <= diff_seconds <= tik_cycle[i * 2 + 1]:
            return True
    return False


# def get_tik_cycle():
#     cycle_conf = [0, 5, 10, 20, 40, 80, 160, 320]
#     result_cycle = []
#     for tik in tik_cycle:
#         result_cycle.append(tik * 60)
#         result_cycle.append(tik * 60 + frequency_config)
#     return result_cycle


if __name__ == '__main__':
    print('begin')
    msc_list = monitor_strategy_config.select_by_stock('000009')
    cur_time = date_util.get_date_time()
    for msc in msc_list:
        trigger_time = dict_util.get_default(msc, StrategyCons.trigger_time, cur_time)
        diff_seconds = int(cur_time.timestamp() - trigger_time.timestamp())
        monitor_strategy_config.update_trigger_by_id(msc.get('id'), cur_time)
    print(in_valid_time(901))
    # deal_trigger(Monitor("600111", "北方稀土"))
    # deal_trigger(Monitor("000063", "中兴通讯"))
    # deal_trigger(Monitor("000157", "中联重科"))
    # deal_trigger(Monitor("600460", "士兰微"))
    # deal_trigger(Monitor("601995", "中金公司"))
    # deal_trigger(Monitor("002612", "航发动力"))
    # deal_trigger(Monitor("603986", "兆易创新"))
    # deal_trigger(Monitor("002466", "天齐锂业"))
    # deal_strategy(Monitor("000063", "中兴通讯"))
    # query_monitor_info()
    # start_task()
